home *** CD-ROM | disk | FTP | other *** search
/ Network PC / Network PC.iso / amiga utilities / communication / bbs / termv4.6 / extras / source / term-source.lha / Accountant.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-18  |  9.9 KB  |  521 lines

  1. /*
  2. **    Accountant.c
  3. **
  4. **    Connection cost accounting
  5. **
  6. **    Copyright © 1990-1996 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. **
  9. **    :ts=4
  10. */
  11.  
  12. #ifndef _GLOBAL_H
  13. #include "Global.h"
  14. #endif
  15.  
  16. /****************************************************************************/
  17.  
  18. enum    { STATE_Idle, STATE_Waiting };
  19.  
  20. /****************************************************************************/
  21.  
  22. enum    { ACMSG_Start,ACMSG_Stop,ACMSG_Query };
  23.  
  24. typedef struct AccountantMsg
  25. {
  26.     struct Message    Message;
  27.  
  28.     UWORD            Type;
  29.     struct timeval    Time;
  30. } AccountantMsg;
  31.  
  32. /****************************************************************************/
  33.  
  34. STATIC struct MsgPort            *AccountantPort;
  35. STATIC struct Task                *AccountantTask;
  36.  
  37. STATIC struct SignalSemaphore     AccountantSemaphore;
  38. STATIC ULONG                     AccountantCost;
  39.  
  40. /****************************************************************************/
  41.  
  42.     /* AccountantEntry(VOID):
  43.      *
  44.      *    The rates accounting task.
  45.      */
  46.  
  47. STATIC VOID __saveds __asm
  48. AccountantEntry(VOID)
  49. {
  50.     struct MsgPort *TimePort;
  51.  
  52.         // Set up the timer resources
  53.  
  54.     if(TimePort = CreateMsgPort())
  55.     {
  56.         struct timerequest *TimeRequest;
  57.  
  58.         if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
  59.         {
  60.             if(!OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,NULL))
  61.             {
  62.                 struct Device *TimerBase;
  63.  
  64.                 TimerBase = TimeRequest->tr_node.io_Device;
  65.  
  66.                     // Now create the communications port
  67.  
  68.                 if(AccountantPort = CreateMsgPort())
  69.                 {
  70.                     struct timeval StopTime;
  71.                     struct timeval Now;
  72.                     ULONG Signals;
  73.                     WORD State;
  74.  
  75.                         // That's me
  76.  
  77.                     AccountantTask = FindTask(NULL);
  78.  
  79.                         // Wake up the main program
  80.  
  81.                     Signal(ThisProcess,SIG_HANDSHAKE);
  82.  
  83.                         // Doing nothing yet...
  84.  
  85.                     State = STATE_Idle;
  86.  
  87.                     FOREVER
  88.                     {
  89.                             // Wait for a sign
  90.  
  91.                         Signals = Wait(PORTMASK(TimePort) | PORTMASK(AccountantPort) | SIG_KILL);
  92.  
  93.                             // Terminate this task?
  94.  
  95.                         if(Signals & SIG_KILL)
  96.                             break;
  97.  
  98.                             // The timer has elapsed?
  99.  
  100.                         if((Signals & PORTMASK(TimePort)) && State == STATE_Waiting)
  101.                         {
  102.                             ULONG Cost;
  103.  
  104.                                 // Terminate the timer request
  105.  
  106.                             WaitIO(TimeRequest);
  107.  
  108.                                 // We are no longer waiting
  109.  
  110.                             State = STATE_Idle;
  111.  
  112.                                 // Start with a clean slate
  113.  
  114.                             Cost = 0;
  115.  
  116.                                 // We want to read the accounting data
  117.  
  118.                             ObtainSemaphoreShared(&PatternSemaphore);
  119.  
  120.                             if(ChosenEntry || ChosenPattern)
  121.                             {
  122.                                     // Look up the current time
  123.  
  124.                                 GetSysTime(&Now);
  125.  
  126.                                     // Check which rate data is current
  127.  
  128.                                 SelectTime(ChosenEntry,ChosenPattern,&Now);
  129.  
  130.                                     // Store the cost for the next unit
  131.  
  132.                                 Cost = PayPerUnit[DT_NEXT_UNIT];
  133.  
  134.                                     // Does this unit last for a certain time?
  135.  
  136.                                 if(SecPerUnit[DT_NEXT_UNIT] > 0)
  137.                                 {
  138.                                     struct timeval TimeVal;
  139.  
  140.                                         // This is the time it takes for the
  141.                                         // unit to elapse
  142.  
  143.                                     TimeVal.tv_secs        = SecPerUnit[DT_NEXT_UNIT] / 10000;
  144.                                     TimeVal.tv_micro    = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
  145.  
  146.                                         // Remember when the unit will have elapsed
  147.  
  148.                                     GetSysTime(&StopTime);
  149.                                     AddTime(&StopTime,&TimeVal);
  150.  
  151.                                         // Start waiting again
  152.  
  153.                                     TimeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  154.                                     TimeRequest->tr_time            = TimeVal;
  155.  
  156.                                     SendIO(TimeRequest);
  157.  
  158.                                     State = STATE_Waiting;
  159.                                 }
  160.                             }
  161.  
  162.                             ReleaseSemaphore(&PatternSemaphore);
  163.  
  164.                                 // Add up the connection cost
  165.  
  166.                             ObtainSemaphore(&AccountantSemaphore);
  167.                             AccountantCost += Cost;
  168.                             ReleaseSemaphore(&AccountantSemaphore);
  169.                         }
  170.  
  171.                             // Somebody wants us to do something...
  172.  
  173.                         if(Signals & PORTMASK(AccountantPort))
  174.                         {
  175.                             AccountantMsg *Message;
  176.  
  177.                                 // For each message...
  178.  
  179.                             while(Message = (AccountantMsg *)GetMsg(AccountantPort))
  180.                             {
  181.                                 switch(Message->Type)
  182.                                 {
  183.                                         // Start waiting?
  184.  
  185.                                     case ACMSG_Start:
  186.  
  187.                                             // Remember the time when this unit
  188.                                             // will have elapsed
  189.  
  190.                                         GetSysTime(&StopTime);
  191.                                         AddTime(&StopTime,&Message->Time);
  192.  
  193.                                             // Start waiting
  194.  
  195.                                         TimeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  196.                                         TimeRequest->tr_time            = Message->Time;
  197.  
  198.                                         SendIO(TimeRequest);
  199.  
  200.                                         State = STATE_Waiting;
  201.  
  202.                                         break;
  203.  
  204.                                         // Stop waiting?
  205.  
  206.                                     case ACMSG_Stop:
  207.  
  208.                                             // Are we currently waiting?
  209.  
  210.                                         if(State == STATE_Waiting)
  211.                                         {
  212.                                             if(!CheckIO(TimeRequest))
  213.                                                 AbortIO(TimeRequest);
  214.  
  215.                                             WaitIO(TimeRequest);
  216.  
  217.                                             State = STATE_Idle;
  218.                                         }
  219.  
  220.                                         break;
  221.  
  222.                                         // Return the time that still has to elapse?
  223.  
  224.                                     case ACMSG_Query:
  225.  
  226.                                         if(State == STATE_Waiting)
  227.                                         {
  228.                                                 // This is now
  229.  
  230.                                             GetSysTime(&Now);
  231.  
  232.                                                 // Is there still time left
  233.                                                 // before the unit elapses?
  234.  
  235.                                             if(CmpTime(&StopTime,&Now) < 0)    // i.e. "StopTime > Now"
  236.                                             {
  237.                                                     // Return the remaining time
  238.  
  239.                                                 Message->Time = StopTime;
  240.  
  241.                                                 SubTime(&Message->Time,&Now);
  242.  
  243.                                                 break;
  244.                                             }
  245.                                         }
  246.  
  247.                                             // Clear the time value
  248.  
  249.                                         memset(&Message->Time,0,sizeof(Message->Time));
  250.  
  251.                                         break;
  252.                                 }
  253.  
  254.                                     // Return the message
  255.  
  256.                                 ReplyMsg(Message);
  257.                             }
  258.                         }
  259.                     }
  260.  
  261.                         // Are we still waiting?
  262.  
  263.                     if(State == STATE_Waiting)
  264.                     {
  265.                         if(!CheckIO(TimeRequest))
  266.                             AbortIO(TimeRequest);
  267.  
  268.                         WaitIO(TimeRequest);
  269.                     }
  270.  
  271.                         // Start cleaning up...
  272.  
  273.                     DeleteMsgPort(AccountantPort);
  274.                 }
  275.  
  276.                 CloseDevice(TimeRequest);
  277.             }
  278.  
  279.             DeleteIORequest(TimeRequest);
  280.         }
  281.  
  282.         DeleteMsgPort(TimePort);
  283.     }
  284.  
  285.         // Finished; wave goodbye and exit
  286.  
  287.     Forbid();
  288.  
  289.     AccountantTask = NULL;
  290.     Signal(ThisProcess,SIG_HANDSHAKE);
  291. }
  292.  
  293. /****************************************************************************/
  294.  
  295.     /* DeleteAccountant():
  296.      *
  297.      *    Stop the accounting task.
  298.      */
  299.  
  300. VOID
  301. DeleteAccountant()
  302. {
  303.     if(AccountantTask)
  304.     {
  305.         Forbid();
  306.  
  307.         SetSignal(0,SIG_HANDSHAKE);
  308.         Signal(AccountantTask,SIG_KILL);
  309.  
  310.         Permit();
  311.     }
  312. }
  313.  
  314.     /* CreateAccountant():
  315.      *
  316.      *    Launch the accounting task.
  317.      */
  318.  
  319. BOOL
  320. CreateAccountant()
  321. {
  322.     InitSemaphore(&AccountantSemaphore);
  323.  
  324.     Forbid();
  325.  
  326.     if(CreateTask("term Accountant Task",MIN(127,ThisProcess->pr_Task.tc_Node.ln_Pri + 10),AccountantEntry,4096))
  327.     {
  328.         SetSignal(0,SIG_HANDSHAKE);
  329.  
  330.         Wait(SIG_HANDSHAKE);
  331.     }
  332.  
  333.     Permit();
  334.  
  335.     return((BOOL)(AccountantTask != NULL));
  336. }
  337.  
  338. /****************************************************************************/
  339.  
  340.     /* QueryAccountantTime(struct timeval *Time):
  341.      *
  342.      *    Query the time that will have to elapse before
  343.      *    the next unit will begin.
  344.      */
  345.  
  346. ULONG
  347. QueryAccountantTime(struct timeval *Time)
  348. {
  349.     AccountantMsg Message;
  350.  
  351.     memset(&Message,0,sizeof(Message));
  352.  
  353.     Message.Message.mn_Length    = sizeof(Message);
  354.     Message.Type                = ACMSG_Query;
  355.  
  356.     SendMessageGetReply(AccountantPort,&Message);
  357.  
  358.     if(Time)
  359.     {
  360.         Time->tv_secs    = Message.Time.tv_secs;
  361.         Time->tv_micro    = Message.Time.tv_micro;
  362.     }
  363.  
  364.     return(Message.Time.tv_secs);
  365. }
  366.  
  367.     /* QueryAccountantCost():
  368.      *
  369.      *    Query the cost of the current connection.
  370.      */
  371.  
  372. ULONG
  373. QueryAccountantCost()
  374. {
  375.     ULONG Cost;
  376.  
  377.     ObtainSemaphoreShared(&AccountantSemaphore);
  378.     Cost = AccountantCost;
  379.     ReleaseSemaphore(&AccountantSemaphore);
  380.  
  381.     return(Cost);
  382. }
  383.  
  384.     /* StopAccountant():
  385.      *
  386.      *    Stop cost accounting and return the current
  387.      *    connection cost.
  388.      */
  389.  
  390. ULONG
  391. StopAccountant()
  392. {
  393.     AccountantMsg Message;
  394.     ULONG Cost;
  395.  
  396.     memset(&Message,0,sizeof(Message));
  397.  
  398.     Message.Message.mn_Length    = sizeof(Message);
  399.     Message.Type                = ACMSG_Stop;
  400.  
  401.     SendMessageGetReply(AccountantPort,&Message);
  402.  
  403.     ObtainSemaphoreShared(&AccountantSemaphore);
  404.     Cost = AccountantCost;
  405.     ReleaseSemaphore(&AccountantSemaphore);
  406.  
  407.     return(Cost);
  408. }
  409.  
  410.     /* StartAccountant(ULONG OnlineSeconds):
  411.      *
  412.      *    Start cost accounting for the current connection.
  413.      */
  414.  
  415. VOID
  416. StartAccountant(ULONG OnlineSeconds)
  417. {
  418.     struct timeval Remain;
  419.     struct timeval Now;
  420.     ULONG Cost;
  421.  
  422.         // Forget the old data
  423.  
  424.     StopAccountant();
  425.  
  426.         // Now is the time for all good men...
  427.  
  428.     GetSysTime(&Now);
  429.  
  430.         // Did the connection happen a few seconds earlier?
  431.  
  432.     if(OnlineSeconds > 0)
  433.     {
  434.         struct timeval Lead;
  435.  
  436.             // Take care of the leading time
  437.  
  438.         Lead.tv_secs    = OnlineSeconds;
  439.         Lead.tv_micro    = 0;
  440.  
  441.             // Technically, the connection happened a bit earlier.
  442.  
  443.         SubTime(&Now,&Lead);
  444.  
  445.             // Choose the right rate accounting time.
  446.  
  447.         SelectTime(ChosenEntry,ChosenPattern,&Now);
  448.  
  449.             // Enter the first rate.
  450.  
  451.         Cost = PayPerUnit[DT_FIRST_UNIT];
  452.  
  453.         Remain.tv_secs    = SecPerUnit[DT_FIRST_UNIT] / 10000;
  454.         Remain.tv_micro    = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
  455.  
  456.             // Did the first unit elapse already?
  457.  
  458.         while(CmpTime(&Lead,&Remain) <= 0)    // i.e. "Lead >= Remain"
  459.         {
  460.                 // Subtract the unit time
  461.  
  462.             SubTime(&Lead,&Remain);
  463.  
  464.                 // Update the "current" time so SelectTime
  465.                 // can do something sensible.
  466.  
  467.             AddTime(&Now,&Remain);
  468.  
  469.             SelectTime(ChosenEntry,ChosenPattern,&Now);
  470.  
  471.             Remain.tv_secs    = SecPerUnit[DT_NEXT_UNIT] / 10000;
  472.             Remain.tv_micro    = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
  473.  
  474.                 // In any case, a new unit starts here
  475.  
  476.             Cost += PayPerUnit[DT_NEXT_UNIT];
  477.         }
  478.  
  479.             // Adjust the remaining time it takes for the unit
  480.             // to elapse.
  481.  
  482.         SubTime(&Remain,&Lead);
  483.     }
  484.     else
  485.     {
  486.             // Choose the right rate accounting time.
  487.  
  488.         SelectTime(ChosenEntry,ChosenPattern,&Now);
  489.  
  490.             // Enter the first rate.
  491.  
  492.         Cost = PayPerUnit[DT_FIRST_UNIT];
  493.  
  494.         Remain.tv_secs    = SecPerUnit[DT_FIRST_UNIT] / 10000;
  495.         Remain.tv_micro    = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
  496.     }
  497.  
  498.         // Store the cost
  499.  
  500.     ObtainSemaphore(&AccountantSemaphore);
  501.     AccountantCost = Cost;
  502.     ReleaseSemaphore(&AccountantSemaphore);
  503.  
  504.         // That's it; "Remain" now holds the time to wait before
  505.         // the first unit elapses. Careful here, we must not
  506.         // launch a `zero' time wait request.
  507.  
  508.     if(Remain.tv_secs > 0 || Remain.tv_micro > 0)
  509.     {
  510.         AccountantMsg Message;
  511.  
  512.         memset(&Message,0,sizeof(Message));
  513.  
  514.         Message.Message.mn_Length    = sizeof(Message);
  515.         Message.Type                = ACMSG_Start;
  516.         Message.Time                = Remain;
  517.  
  518.         SendMessageGetReply(AccountantPort,&Message);
  519.     }
  520. }
  521.